package org.gbif.map
{
	import com.google.maps.Color;
	import com.google.maps.Copyright;
	import com.google.maps.CopyrightCollection;
	import com.google.maps.LatLng;
	import com.google.maps.LatLngBounds;
	import com.google.maps.Map;
	import com.google.maps.MapMouseEvent;
	import com.google.maps.TileLayerBase;
	import com.google.maps.interfaces.IPane;
	import com.google.maps.overlays.EncodedPolylineData;
	import com.google.maps.overlays.Polygon;
	import com.google.maps.overlays.PolygonOptions;
	import com.google.maps.styles.FillStyle;
	import com.google.maps.styles.StrokeStyle;
	
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.utils.Dictionary;
	
	import gs.TweenLite;
	
	import mx.collections.ArrayCollection;
	import mx.core.Application;
	import mx.formatters.NumberFormatter;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.remoting.mxml.RemoteObject;
	
	public class GbifPolygonOccurrenceGridTileLayer extends TileLayerBase {
		
		private var tileSize:Number=256;
		private var map:Map;
		private var mapContainerComponent:mapCanvas;
		private var srvMapGrid:RemoteObject;
		private	var paletteOne:Array =   			[0xFBFF9D,0xFFE86A,0xFFBF49,0xFF6727,0xFF2614,0xDB0000,0xD36800,0xE20F00,0xC78F00,0xD04B00,0xD60000];
		private	var paletteYellowRed10:Array =   	[0xFABA00,0xFAB000,0xFD8D00,0xF97A00,0xFC5D0E,0xFD3F16,0xFC3117,0xFE211D,0xFE091F,0xFC001F];
		private	var paletteTwo:Array =   			[0xB9B5C0,0x818181,0x67674F,0x8C8B44,0xA7A626,0xBDBB00,0xC99200,0xD36800,0xD3411E,0xE20F00];  
		private	var paletteThree:Array = 			[0x232859,0x1E4F65,0x1C666A,0x1A794F,0x219015,0x69A90B,0xC2BA00,0xC2BA00,0xC78F00,0xD04B00,0xD60000];  
		
		private var loadedTiles:Dictionary = new Dictionary();
		private var cacheRequest:Dictionary = new Dictionary();
		private var polygonCache:Dictionary = new Dictionary();
		private var gridPane:IPane;		
		
		private var previousClickedPolygon:Polygon;
		private var previousClickedPolygonOptions:PolygonOptions;
		
		public var currentZoomLevel:Number;
		public var previousZoomLevel:Number;
		private var previousZoomLevelCleared:Boolean=false;
		private var numFormatter:NumberFormatter= new NumberFormatter();
		private var polygonId:Number;
		
		
		
		
		public function GbifPolygonOccurrenceGridTileLayer(polygonId:Number, map:Map, serviceEndPoint:String,mapContainerComponent:mapCanvas) {
			srvMapGrid= new RemoteObject("wdpaManager");
			srvMapGrid.endpoint = serviceEndPoint;
			srvMapGrid.showBusyCursor=false;
			srvMapGrid.addEventListener(ResultEvent.RESULT,srvMapGridResultHandler);
			srvMapGrid.addEventListener(FaultEvent.FAULT,srvMapGridFaultHandler);
			  
			var copyrightCollection:CopyrightCollection = new CopyrightCollection();
			copyrightCollection.addCopyright(new Copyright("ennefox", new LatLngBounds(new LatLng(-180, 90), new LatLng(180, -90)), 21,"ennefox"));
			super(copyrightCollection, 0, 21);
			
			for (var i:Number=0;i<=17;i++) {
            	loadedTiles[i]=new Dictionary();
            }
			
			this.map = map;		
			this.mapContainerComponent=mapContainerComponent;
			this.polygonId = polygonId;
				
			currentZoomLevel=map.getZoom();
			gridPane= map.getPaneManager().createPane();
			
			numFormatter.useThousandsSeparator=true;
			
		}

		public override function loadTile(tilePos:Point, zoom:Number):DisplayObject {
		    
		    if (currentZoomLevel!=zoom) {
				previousZoomLevel=currentZoomLevel;		
				previousZoomLevelCleared=false;    	
		    }
		    currentZoomLevel=zoom;
		    
		    if (!cacheRequest[tilePos.x+"/"+tilePos.y+"/"+zoom]) {
		    	Application.application.mapCanvas.dataLoading=true;
		    	cacheRequest[tilePos.x+"/"+tilePos.y+"/"+zoom]=true;
			    srvMapGrid.getGoogleTileGrids(tilePos.x,tilePos.y,zoom,polygonId);
		    } else {
		    	//it is already on the cache
				for each (var pol:Polygon in loadedTiles[zoom][tilePos.x+"/"+tilePos.y]) {
					addPolygonSmooth(pol);
				}		
				
				clearPolygonsFromZoomLevel(previousZoomLevel); 	
		    }
		    
			
			return new Sprite();
		}
		
		private function srvMapGridFaultHandler(ev:FaultEvent):void {
			trace(ev.message);
		}
		

	    private function srvMapGridResultHandler(ev:ResultEvent):void {
	    	Application.application.mapCanvas.dataLoading=false;
 			var gridData:ArrayCollection = ev.result.grids as ArrayCollection;
 			var ta:Array = loadedTiles[ev.result.params.zoom][ev.result.params.tileX+"/"+ev.result.params.tileY]= new Array();
 			var z:Number=ev.result.params.zoom;
 			if(z<3)
 				z=2;
		 	for each(var cell:Object in gridData) {
		 		//num_occ,num_specimens,num_observations,num_other_bor,num_plantae,num_animalia,num_other_kingdoms
		 		var stats:Array=[cell.num_occ,cell.num_specimens,cell.num_observations,cell.num_other_bor,cell.num_plantae,cell.num_animalia,cell.num_other_kingdoms];
		 		var poli:Polygon;
		 		if (cell.border_geom != null && cell.border_geom !='') {
		 			var encodedPoints:String = cell.border_geom.encodedPoints;
    				var encodedLevels:String = cell.border_geom.encodedLevels;
			 		poli = Polygon.fromEncoded([new EncodedPolylineData(encodedPoints, 2, encodedLevels, 18)],getPolygonOptions(stats,ev.result.params.tileLimits.source as Array));
		 		} else {
			 		poli = new Polygon(getPolygonCoordsFromTileCoords(cell.tilex,cell.tiley,z+2),getPolygonOptions(stats,ev.result.params.tileLimits.source as Array));
		 		}
		 		
		 		poli.addEventListener(MapMouseEvent.CLICK,function(ev:MapMouseEvent):void {
		 			
		 			if(previousClickedPolygon !=null) {
		 				previousClickedPolygon.setOptions(previousClickedPolygonOptions);
		 			}
		 			previousClickedPolygon=ev.target as Polygon;
		 			previousClickedPolygonOptions = previousClickedPolygon.getOptions();
		 			
		 			(ev.target as Polygon).setOptions(new PolygonOptions({fillStyle: new FillStyle({color:Color.BLACK})}));
		 			
		 			mapContainerComponent.onPolygonClicked(polygonCache[ev.target] as Point,currentZoomLevel);
		 			
		 		});
		 		
		 		poli.foreground.alpha=0;
		 		ta.push(poli);
		 		polygonCache[poli]=new Point(cell.tilex,cell.tiley);
		 		if (ev.result.params.zoom == map.getZoom()) {
					addPolygonSmooth(poli);
		 		}
			}	 			
		    //clear previous polygons
			clearPolygonsFromZoomLevel(previousZoomLevel);
		
	    }
	      
	    private function addPolygonSmooth(pol:Polygon):void {
	    	pol.foreground.alpha=0;
	    	gridPane.addOverlay(pol);
	    	TweenLite.to(pol.foreground,1.0,{alpha:0.6});
	    }  
	      
	    private function clearPolygonsFromZoomLevel(zoom:Number):void {
			if(!previousZoomLevelCleared && !isNaN(previousZoomLevel)) {
				previousZoomLevelCleared=true;
	 			for each (var zl2:Array in loadedTiles[zoom]) {
					for each(var pol2:Polygon in zl2) {
						TweenLite.to(pol2.foreground,2.5,{alpha:0, onComplete:onFinishTween,onCompleteParams:[pol2]});
					}	
				}  	    	
			}   	    	
	    }  
	      
	      
		private	function onFinishTween(pol:Polygon):void {
			    gridPane.removeOverlay(pol);
		}  	    
	    
	    
	    
	    private function getPolygonCoordsFromTileCoords(x:Number,y:Number,zoom:Number):Array {
	    	  zoom = 17 - zoom;
		      var lon:Number      = -180; // x
		      var lonWidth:Number = 360; // width 360
		      var lat:Number       = -1;
		      var latHeight:Number = 2;
		
		      var tilesAtThisZoom:Number = 1 << (17 - zoom);
		      lonWidth  = 360.0 / tilesAtThisZoom;
		      lon       = -180 + (x * lonWidth);
		      latHeight = -2.0 / tilesAtThisZoom;
		      lat       = 1 + (y * latHeight);
		
		      // convert lat and latHeight to degrees in a transverse mercator projection
		      // note that in fact the coordinates go from about -85 to +85 not -90 to 90!
		      latHeight += lat;
		      latHeight = (2 * Math.atan(Math.exp(Math.PI * latHeight))) - (Math.PI / 2);
		      latHeight *= (180 / Math.PI);
		
		      lat = (2 * Math.atan(Math.exp(Math.PI * lat))) - (Math.PI / 2);
		      lat *= (180 / Math.PI);
		
		      latHeight -= lat;
		
		      if (lonWidth < 0) {
		         lon      = lon + lonWidth;
		         lonWidth = -lonWidth;
		      }
		
		      if (latHeight < 0) {
		         lat       = lat + latHeight;
		         latHeight = -latHeight;
		      }

		      return [
		      	new LatLng(lat,lon),
		      	new LatLng(lat+latHeight,lon),
		      	new LatLng(lat+latHeight,lon+lonWidth),
		      	new LatLng(lat,lon+lonWidth),
		      	new LatLng(lat,lon)];
	    }
	    
	    private function getPolygonOptions(stats:Array,ranges:Array):PolygonOptions {
	    	
	    	//Stats: num_occ,num_specimens,num_observations,num_other_bor,num_plantae,num_animalia,num_other_kingdoms
	    	
	    	var numTrams:Number = ranges.length;
	    	var tram:Number=0;
	    	for each(var r:Object in ranges) {
	    		if(stats[0]>=r.low && stats[0]<r.up) {
	    			break;
	    		}
	    		tram++;
	    	}
	    	
	    	var colNumber:Number = tram/(numTrams-1); 	    	
	    	if(colNumber>1)
	    		colNumber=1;
	    		
  	    	var palette:Array = paletteYellowRed10;
  	    	var l:Number =Math.floor(colNumber*10) -1;
  	    	if(l<0)
  	    		l=0;
  	    	var col:Number = palette[l];								
	
			//Stats: num_occ,num_specimens,num_observations,num_other_bor,num_plantae,num_animalia,num_other_kingdoms
			var toolTip:String = "";
 			toolTip+="<b><p align=\"center\" size=\"15\">"+numFormatter.format(stats[0]) +" occ.</b></p>";

			var spePer:Number =  Math.round((stats[1]/stats[0])*100);
			var obsPer:Number =  Math.round((stats[2]/stats[0])*100);
			var otherBorPer:Number =  100-spePer-obsPer;
						
			var plantPer:Number =  Math.round((stats[4]/stats[0])*100);
			var AnimPer:Number =  Math.round((stats[5]/stats[0])*100);
			var OtherKingPer:Number =  100-plantPer-AnimPer;
			
			var chart1:String = "http://chart.apis.google.com/chart?chs=150x60&cht=bhs&chco=7EA724,A9C248,D5D18A" + 
					"&chd=t:"+plantPer+"|"+AnimPer+"|"+OtherKingPer+"&chxt=x,t&chxl=0:|Plants|Animals|Other|1:|"+plantPer+"%|"+AnimPer+"%|"+OtherKingPer+"%|&chxs=0,000000,9|1,000000,10,-1&chf=bg,s,FFFF9E"

			var chart2:String = "http://chart.apis.google.com/chart?chs=150x60&cht=bhs&chco=A9252A,C44950,CA6E6F" + 
					"&chd=t:"+spePer+"|"+obsPer+"|"+otherBorPer+"&chxt=x,t&chxl=0:|Spec.|Observ.|Other|1:|"+spePer+"%|"+obsPer+"%|"+otherBorPer+"%|&chxs=0,000000,9|1,000000,10,-1&chf=bg,s,FFFF9E"

			toolTip+="\n<img id=\""+chart1+"\" src=\""+chart1+"\" />";
			toolTip+="<img id=\""+chart2+"\" src=\""+chart2+"\" />";
			
			
			return new PolygonOptions({
					strokeStyle: new StrokeStyle({
			        	color: 0x818181,
			        	thickness: 0,
			        	alpha: 1}), 
			        fillStyle: new FillStyle({
			        	color: col,
			        	alpha:1}),
			        tooltip: toolTip
			        
			});	    	
	    }
    
	}
}